package mbp.agents;

import jade.core.Agent;
import jade.core.behaviours.TickerBehaviour;
import jade.lang.acl.ACLMessage;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;

import mbp.agents.AMbpBaseAgent.AgentState;
import mbp.base.Activity;
import mbp.base.ContractState;
import mbp.base.MessageType;
import mbp.base.ServiceType;
import mbp.base.Settings;
import mbp.core.Contract;
import mbp.core.CounterOffer;
import mbp.core.Intention;
import mbp.core.Offer;
import mbp.core.Resource;
import mbp.core.ResourceValidation;
import mbp.core.Service;
import mbp.exceptions.ParseExpressionErrorException;
import mbp.message.Message;
import mbp.message.ObjectBuilder;
import mbp.message.extra;
import mbp.message.resource;
import mbp.message.server;


/**
 * Implementuje schemat dzialania Agenta Serwera. W TRAKCIE REALIZACJI !
 * @author Wojciech Wilanski
 *
 */
public abstract class AMbpServerAgent extends AMbpAgentMap
{
	//Nazwa agenta: magazynu zasobow
	protected String storageName = Settings.storageName;
	
	//Typ uslugi oferowany przez agenta
	protected String serviceType;
	
	//Wzorcowa us�uga
	protected Service patternService;
	
	//Informacje o zasobach bedacych w magazynie
	//private Map<String, resource> resourceInfo;
	
	//Lista uslug tego samego typu oferowanych przez agenta.
	//Uslugi maja rozna wartosci cech zasobow we/wy
	//private List<Service> servicesList;
	
	//Lista funkcji uzytecznosci
	//private Map<String, ResourceUtility> utilitiesList;
	
	//Lista na kontrakty w trakcie realizacji
	protected Map<String, Contract> contractsList;
	
	//Lista na kontrakty archiwalne, zrealizaowane/zerwane
	private Map<String, Contract> contractsArchiv;
	
	//Lista aktywnych ofert
	private Map<String, Offer> offersList;
	
	//Lista aktywnych ofert
	//private Map<String, Offer> offersList;
	
	//Referencja na odebrana intencja
	private Intention intention;
	
	//Referencja na odebrana kontroferte
	private CounterOffer counterOffer;
	
	//Referencja na wlasna kontroferte (przygotowana przez serwer)
	private CounterOffer myCounterOffer;
	
	//inicjalizacja loggera
	protected Logger logger = Logger.getLogger(this.getClass());
	
	private ServiceServerTicker serviceTicker;
		
	
	@Override
	protected void setup()
	{
		super.setup();
		
		serviceType = null;
		//servicesList = Collections.synchronizedList(new ArrayList<Service>());
		//resourceInfo = Collections.synchronizedMap(new HashMap<String, resource>());
		
		offersList = Collections.synchronizedMap(new HashMap<String, Offer>());
		contractsList = Collections.synchronizedMap(new HashMap<String, Contract>());
		contractsArchiv = Collections.synchronizedMap(new HashMap<String, Contract>());
		
		serviceTicker = new ServiceServerTicker(this, 1000);
		addBehaviour(serviceTicker);
		
		logger.info("Agent " + getAID().getName() + " is ready.");
	}
	
	/**
	 * Rejsetruje swoje uslugi w rejestrze
	 * @param agentName
	 */
	private void registerServer(String agentName)
	{
		//Wpisanie do rejestu
		Message msg = new Message(MessageType.REGISTER, getLocalName(), agentName);
		List<server> serverList = new ArrayList<server>();
		server srv = new server();
		srv.name = getLocalName();
		srv.type = serviceType;
		serverList.add(srv);
		
		List<extra> extList = new ArrayList<extra>();
		//extList.add(ext);
		
		ServiceType sType = patternService.getType();
		for(int i = 0; i<sType.getResourceTypesOut().size(); i++)
		{
			extra ext = new extra();
			ext.name  = "resource";
			ext.value = sType.getResourceTypesOut().get(i).getResourceTypeName();
			extList.add(ext);
		}
		
		msg.getContentXml().servers = serverList;
		msg.getContentXml().extras = extList;
		SenderLogger.send(this, msg.GetACLMessage());
	}
	
	@Override
	protected void processMessage(mbp.message.Message iMsg, String senderName)
	{
		MessageType messageType = iMsg.getMessageType();
		switch(messageType)
		{
		//Odebranie intencji
		case INTENTION:
			onIntention(iMsg, senderName);
			break;
		case COUNTEROFFER:
			onCounterOffer(iMsg, senderName);
			break;
		case ACCEPTOFFER:
			onAcceptOffer(iMsg, senderName);
			break;
		case REJECT:
			onReject(iMsg, senderName);
			break;
		//Odebranie uslugi wzorcowej
		case SERVICE:
			onService(iMsg, senderName);
			break;
		case SERVICELGET:
			onServiceLGet(iMsg, senderName);
			break;
		case SERVICEGET:
			onServiceLGet(iMsg, senderName);
			break;
		case UTILITY:	
			break;
		case RESOURCEL:
			//onResourceL(iMsg, senderName);
			break;
		case FREEZE:
			onFeeze(iMsg, senderName);
			break;
		case RESUME:
			onResume(iMsg, senderName);
			break;
		case DODELETE:
			onDoDelete(iMsg, senderName);
			break;
		default:
			//logger.warn("Message type: " + messageType + " not supported");
			break; 
		}
	}
	
//	/**
//	 * Obsluga odebranej wiadomosci typu RESOURCEL
//	 * @param iMsg
//	 * @param senderName
//	 */
//	private void onResourceL(mbp.message.Message iMsg, String senderName)
//	{
//		List<resource> resourceL = iMsg.getContentXml().resources;
//		resourceInfo.clear();
//		for(int i = 0; i<resourceL.size(); i++)
//		{
//			String key = new String(resourceL.get(i).resourceid);
//			resourceInfo.put(key, resourceL.get(i));
//			logger.debug("Aded resource info: " + key);
//		}
//	}
	
	
	
	/**
	 * Odebrano intencje od klienta
	 * @param intention
	 * @param agentName
	 */
	private void onIntention(mbp.message.Message iMsg, String senderName)
	{
		Intention intention = iMsg.getIntention();
		String srvType = intention.getServiceType().getServiceTypeName();
		logger.info("Recieved INTENTION for service type: " + srvType);
		
		//Sprawdzenie czy intencja dotyczy typu serwisu oferowanego przez serwer
		if(serviceType.compareTo(srvType)!=0)
		{
			logger.error("Wrong Service type! Service type should be: " + serviceType);
			return;
		}
		
		this.intention = intention;
		processNegotiation(Activity.CLIENT, MessageType.INTENTION, null);	
	}
	
	/**
	 * Odebrano kontroferte od klienta
	 * @param counterOffer
	 * @param agentName
	 */
	private void onCounterOffer(mbp.message.Message iMsg, String senderName)
	{
		logger.debug("Called onCounterOffer");
		this.counterOffer = iMsg.getCounterOffer();
		processNegotiation(Activity.CLIENT, MessageType.COUNTEROFFER, this.counterOffer.GetUUID());
	}
	
	private void onService(mbp.message.Message iMsg, String senderName)
	{
		logger.debug("Called onService");
		//Odczytanie typu uslugi
		String type = iMsg.getContentXml().services.get(0).type;
		
		//Jezeli jes to pierwsza usluga, serwer jest inicjowany na swiadczenie tylko tego typu uslug.
		if(serviceType == null)
		{
			Service service = iMsg.getService();
			serviceType = type;
			patternService = service;
			logger.info("Recieved SERVICE - Recieved first pattern Service. From now offered service type is: " + serviceType);
			
			registerServer(Settings.registerName);
		}
		else
		{
			if(serviceType.compareTo(type)==0)
			{
				Service service = iMsg.getService();
				serviceType = type;
				patternService = service;
				logger.info("Recieved SERVICE: " + serviceType + " - Pattern service has been changed.");
			}
			else
			{
				logger.error("Server agent already initialized. Service type is: " + serviceType);
			}
		}
		
	}
	
	private void onServiceLGet(mbp.message.Message iMsg, String senderName)
	{
		Message iMsgResp = new Message(MessageType.SERVICE, this.getLocalName(), senderName);
		if(this.patternService != null) {
			iMsgResp.setService(this.patternService);
		}
		SenderLogger.send(this, iMsgResp.GetACLMessage());
	}
	
	/**
	 * Obsluga checi podpisania kontraktu przez agenta podstawowego
	 * @param iMsg
	 * @param senderName
	 */
	private void onAcceptOffer(mbp.message.Message iMsg, String senderName)
	{
		// Sprawdzenie oferty, czy jest na liscie waznych ofert
		String uuid = iMsg.getContentXml().extras.get(0).value;		
		if(!offersList.containsKey(uuid))
		{
			logger.error("No valid offer for recieved uuid: " + uuid + " found.");
			return;
		}
		
		// Podjecie decyzji przez serwer
		if(decisionContract(offersList.get(uuid)))
		{
			Offer offer = offersList.remove(uuid);
			prepareSignContract(offer);
		}
		else
		{
			offersList.remove(uuid);
			sendReject(senderName, uuid, null);
		}
	}
	
	/**
	 * Odebrano chec zerwania negocjacji od klienta. Nastepuje usuniecie waznej oferty
	 * z listy aktywnych ofert o podanym uuid.
	 * @param iMsg
	 * @param senderName
	 */
	private void onReject(mbp.message.Message iMsg, String senderName)
	{
		logger.debug("Called onReject");
		if(iMsg.getContentXml().extras.size() > 0)
		{
			extra ex = iMsg.getContentXml().extras.get(0);
			if(ex.name.compareTo("uuid")==0)
			{
				String uuid = ex.value;
				
				//zerwanie negocjacji, odrzucenie oferty
				if(offersList.containsKey(uuid))
				{
					offersList.remove(uuid);
					logger.info("Removed offer " + uuid);
				}
				
				//zerwanie renegocjacji kontraktu przez klienta
				if(contractsList.containsKey(uuid))
				{
					Contract contract = contractsList.get(uuid);
					//if(contract.getContractState() == ContractState.FROZEN)
					//{
					//	contract.resume();
					//	sendContract(contract);
					//}
					contract.setContractState(ContractState.REJECTED);
					//contract.setContractState(ContractState.BROKEN);
					//contractsArchiv.put(uuid, contract);
					//processNegotiation(Activity.CLIENT, MessageType.REJECT, uuid);
				}
			}
		}
	}
	
	/**
	 * Obsluga odebranej wiadomoci FREEZE - Wstrzymaj kontrakt
	 * @param iMsg
	 * @param senderName
	 */
	private void onFeeze(mbp.message.Message iMsg, String senderName)
	{
		extra ex = iMsg.getContentXml().extras.get(0);
		String uuid = ex.value;
		if(contractsList.containsKey(uuid))
		{
			Contract contract = contractsList.get(uuid);
			contract.suspend();
			sendContract(contract);		
		}
	}
	
	/**
	 * Obsluga odebranej wiadomoci RESUME - Wznow kontrakt
	 * @param iMsg
	 * @param senderName
	 */
	private void onResume(mbp.message.Message iMsg, String senderName)
	{
		extra ex = iMsg.getContentXml().extras.get(0);
		String uuid = ex.value;
		if(contractsList.containsKey(uuid))
		{
			Contract contract = contractsList.get(uuid);
			contract.resume();
			sendContract(contract);
		}
	}
	
	
	/**
	 * Odebrano zadanie zakonczenia pracy serwera. Wszystkie aktywne kontrakty musza zostac
	 * zerwane a zasoby zwrocone do odpowiednich agentow
	 * @param iMsg
	 * @param senderName
	 */
	private void onDoDelete(mbp.message.Message iMsg, String senderName)
	{
		if(senderName.compareTo(Settings.managerName)!=0)
		{
			logger.error("Only agent: " + Settings.managerName + " is authorized do delete me");
			return;
		}
		
		// Zatrzymanie pracy zwiazanej z pratwarzaniem ofert i kontraktow 
		removeBehaviour(serviceTicker);
		
		// Usuniecie wszystkich ofert
		offersList.clear();
		
		Iterator<String> it = contractsList.keySet().iterator();
		while(it.hasNext())
		{
			String key = it.next();
			Contract contract = contractsList.get(key);
			ContractState contractState = contract.getContractState();
			
			if(contractState == ContractState.PRODUCE || contractState == ContractState.REALIZE)
			{
				List<Resource> rcToGiveBack = new ArrayList<Resource>();
				String agentName = contract.getAgentName();
				String clientName = contract.getClientName();
				
				Iterator<String> itRc = resourceList.keySet().iterator();
				while(itRc.hasNext())
				{
					String rcKey = itRc.next();
					Resource rc = resourceList.get(rcKey);
					if(rc.getOwner().compareTo(clientName)==0)
					{
						rcToGiveBack.add(rc);
						itRc.remove();
					}
				}
				
				sendReject(agentName, contract.GetUUID(), rcToGiveBack);
			}
		}
		
		contractsList.clear();
	}
	
	/**
	 * Utworzenie kontraktu na podstawie zaakceptowanej oferty i przygotowanie do podpisania.
	 * @param offer - Oferta
	 */
	private void prepareSignContract(Offer offer)
	{
		Contract contract = new Contract(offer);
		String owner = contract.getClientName();
		
		// Wyslanie do magazynu po zaliczke
		for(int i = 0; i<contract.getOffer().getResourcesIn(0).size(); i++)
		{
			int quantity = contract.getOffer().getResourcesIn(0).get(i).getQuantity();
			if(quantity>0)
			{
				ResourceValidation rcv = contract.getOffer().getResourcesIn(0).get(i);					
				sendResourceGet(storageName, owner, rcv);
				contract.counter++;
				logger.info("Sending request for resource advance: " + rcv.getType().getResourceTypeName() + " to " + storageName);
			}
		}
		// String client = contract.getClientName();
		String cnUuid = new String(contract.GetUUID());
		contractsList.put(cnUuid, contract);
	}
	
	/**
	 * Usuwa z listy zasobow serwera i zwraca liste zasobow pobranych jako towar do realizacji kontraktu
	 * @param clientName - Nazwa klienta kontraktu
	 * @param contract - Lista zasobow
	 * @return
	 */
	private List<Resource> getRecievedStuff(Contract contract)
	{
		List<Resource> rcToGiveBack = new ArrayList<Resource>();
		
		List<ResourceValidation> rcvList = contract.getOffer().getResourcesIn(1);
		
		for(int i = 0; i<rcvList.size(); i++)
		{
			ResourceValidation rcv = rcvList.get(i);
			String type = rcv.getType().getResourceTypeName();
			String owner = contract.getClientName();
			
			Iterator<String> it = resourceList.keySet().iterator();
			while(it.hasNext())
			{
				String uuid = it.next();
				Resource rc = resourceList.get(uuid);
				if((rc.getType().getResourceTypeName().compareTo(type) == 0) && (rc.getOwner().compareTo(owner) == 0))
				{
					try {
						if(rcv.validateCondition(rc))
						{
							rcToGiveBack.add(rc);
							it.remove();
							break;
						}
					} catch (ParseExpressionErrorException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}			
			}
		}
		
		return rcToGiveBack;
	}
	
	/**
	 * Zmiana wlasciciela zasobow bedacych w posiadaniu serwera. Metoda wykorzystywana przy zakanczaniu kontraktow.
	 * @param oldOwner - Obecny wlasciciel
	 * @param newOwner - Nowy wlasciciel
	 */
	public void changeOwner(String oldOwner, String newOwner)
	{
		Iterator<String> it = resourceList.keySet().iterator();
		while(it.hasNext())
		{
			String uuid = it.next();
			Resource rc = resourceList.get(uuid);
			if(rc.getOwner().compareTo(oldOwner)==0)
			{
				rc.setOwner(new String(newOwner));
			}
		}
	}
	
	/**
	 * Decyzja po odebraniu intencji od klienta
	 * @param intention
	 * @return
	 */
	public abstract Service decision(Intention intention);
	
	/**
	 * Decyzja po odebraniu kontroferty do wyslanej wczesniej oferty
	 * @param cOffer
	 * @param lastOffer
	 * @return
	 */
	public abstract boolean decision(CounterOffer cOffer, Offer lastOffer);
	
	/**
	 * Decyzja po odebraniu kontroferty do realizowanego kontraktu (renegocjacja)
	 * @param cOffer
	 * @param contract
	 * @return
	 */
	public abstract boolean decision(CounterOffer cOffer, Contract contract);
	
	/**
	 * Decyzja w sprawie opdpisania kontraktu do podanej oferty. Klient zgodzil sie na oferte.
	 * @param offer
	 * @return
	 */
	public abstract boolean decisionContract(Offer offer);
	
	/**
	 * Przygotowanie oferty do odebranej intencji
	 * @param intention
	 * @return
	 */
	public abstract Offer prepareOffer(Intention intention, Service service);
	
	/**
	 * Przygotowanie oferty do odebranej kontroferty
	 * @param coffer
	 * @param lastOffer
	 * @return
	 */
	public abstract Offer prepareOffer(CounterOffer coffer, Offer lastOffer);
	
	/**
	 * Przygotowanie oferty do realizowanego kontraktu (nowe warunki renegocjacja)
	 * @param cOffer
	 * @param contract
	 * @return
	 */
	public abstract Offer prepareOffer(CounterOffer cOffer, Contract contract);
	
	
	
	/**
	 * Wejscie do procedury negocjacja. Jezeli uuid jest obecny, oznacza to ze sprawa dotyczy
	 * istniejacego juz obiektu: wyslanej oferty lub realizowanego kontraktu.
	 * Jezeli inicjatorem jest Serwer to zawsze chodzi o posiadany przez serwer obiekt.
	 * Jezeli inicjatorem jest Klient, to moze chodzic o: Odebrana intencje, odebrana kontroferta
	 * @param sender
	 * @param mType
	 * @param uuid
	 */
	private void processNegotiation(Activity sender, MessageType mType, String uuid)
	{
		switch(sender)
		{
		// Start z inicjatywy klienta ------------------------------------------.
		case CLIENT:
			switch(mType)
			{
			case INTENTION:
				Intention recievedIntention = this.intention;
				this.intention = null;
				
				String agentName = recievedIntention.getAgentName();
				// Podjecie decyzji: wyslanie oferty / rezygnacja
				Service service = decision(recievedIntention);
				if( service != null )
				{
					Offer offer = prepareOffer(recievedIntention, service);
					sendOffer(offer);
				}
				else
				{
					sendNoOffer(agentName);
				}
				break;
			case COUNTEROFFER:
				CounterOffer recievedCOffer = this.counterOffer;
				this.counterOffer = null;
				
				String clientName = recievedCOffer.getClientName();
				
				// W przypadku gdy kontroferta dotyczy wyslanej oferty.
				if(offersList.containsKey(uuid))
				{
					// Podjecie decyzji
					if(decision(recievedCOffer, offersList.get(uuid)))
					{
						Offer offer = prepareOffer(recievedCOffer, offersList.get(uuid));
						offer.setServerName(getLocalName());
						offer.setClientName(clientName);
						sendOffer(offer);
					}
					else
					{
						// W przypadku rezygnacji oferta jest usuwana z listy
						offersList.remove(uuid);
						sendReject(recievedCOffer.getAgentName(), uuid, null);
					}		
				}
				
				// W przypadku gdy kontroferta dotyczy realizowanego kontraktu
				if(contractsList.containsKey(uuid))
				{
					// Podjecie decyzji w sprawie renegocjacji kontraktu
					if(decision(recievedCOffer, contractsList.get(uuid)))
					{
						Offer offer = prepareOffer(recievedCOffer, contractsList.get(uuid));
						offer.setServerName(getLocalName());
						offer.setClientName(clientName);
						sendOffer(offer);
					}
					else
					{
						//Contract contract = contractsList.remove(uuid);
						//contract.setContractState(ContractState.BROKEN);
						//contractsArchiv.put(uuid, contract);
						//sendReject(contract.getAgentName(), uuid, null);
					}
				}
				break;
			case REJECT:
				// W przypadku gdy zerwanie wspolpracy dotyczy wyslanej oferty.
				if(offersList.containsKey(uuid))
				{
					offersList.remove(uuid);
				}
				// W przypadku gdy zerwanie wspolpracy dotyczy realizowanego kontraktu
				if(contractsList.containsKey(uuid))
				{
					Contract contractToR = contractsList.get(uuid);
					contractToR.setContractState(ContractState.REJECTED);
					//sendContract(contractToR);
					List<Resource> rcToGiveBack = getRecievedStuff(contractToR);
					
					sendReject(new String(contractToR.getAgentName()), uuid, rcToGiveBack);
					//contractsArchiv.put(new String(uuid), contractToR);
					
					changeOwner(contractToR.getClientName(), getLocalName());
				}
				break;
			}				
			break;//case CLIENT
		//Start z inicjatywy serwera - decyzja nie musi byc podejmowana bo serwer juz ja podjal
		case SERVER:
			switch(mType)
			{
			case OFFER:
				Contract contract = contractsList.get(uuid);
				Offer offer = prepareOffer(this.myCounterOffer, contract);
				offer.setServerName(getLocalName());
				offer.setClientName(contract.getClientName());
				sendOffer(offer);
				break;
			case REJECT:
				break;
			}
			break;//case SERVER:
		}
	}
	
	
	/**
	 * Podpisanie kontraktu
	 * @param uuid
	 */
	public void processAcceptOffer(mbp.message.Message iMsg, String senderName)
	{
		logger.debug("Process accept offer started ...");
		//sprawdzenie oferty
		String uuid = iMsg.getContentXml().offers.get(0).uuid;
		if(!offersList.containsKey(uuid))
		{
			logger.error("No valid offer for recieved uuid: " + uuid + " found. Can not sign contract.");
			return;
		}
		
		Offer offer = offersList.get(uuid);
		Contract contract = new Contract(offer);
		String owner = contract.getClientName();
		
		
		// Proba pobrania z magazynu zasobow potrzebnych jako zaliczka
		for(int i = 0; i<contract.getOffer().getResourcesIn(0).size(); i++)
		{
			ResourceValidation rcv = contract.getOffer().getResourcesIn(0).get(i);		
			sendResourceGet(storageName, owner, rcv);
			logger.info("Sending request for resource: " + rcv.getType().getResourceTypeName() + " to " + storageName);
		}
		
		String cnUuid = new String(contract.GetUUID());
		contractsList.put(cnUuid, contract);
	}
	
	
	private void sendOffer(Offer offer)
	{
		String key = offer.GetUUID();
		offersList.put(key, offer);
		
		String agentName = offer.getAgentName();
		Message iMsg = new Message(MessageType.OFFER, this.getLocalName(), agentName);
		iMsg.setOffer(offer);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	/**
	 * Wyslanie informacji o kontrakcie do klienta
	 * @param contract
	 * @param agentName
	 */
	private void sendContract(Contract contract)
	{
		String agentName = contract.getAgentName();
		
		Message iMsg = new Message(MessageType.CONTRACT, this.getLocalName(), agentName);

		ObjectBuilder obb = new ObjectBuilder();
		mbp.message.contract cntr = obb.getContractInfo(contract);
				
		iMsg.getContentXml().contracts.add(cntr);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	private void sendAcceptContract(Contract contract)
	{
		String agentName = contract.getAgentName();
		
		Message iMsg = new Message(MessageType.ACCEPTCONTRACT, this.getLocalName(), agentName);
		ObjectBuilder obb = new ObjectBuilder();
		
		mbp.message.contract cntr = obb.getContractInfo(contract);
		
		iMsg.getContentXml().contracts.add(cntr);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	private void sendDone(Contract contract, List<Resource> rcProduced)
	{
		String agentName = contract.getAgentName();
		Message iMsg = new Message(MessageType.DONE, this.getLocalName(), agentName);
		
		ObjectBuilder obb = new ObjectBuilder();
		
		for(int i = 0; i<rcProduced.size(); i++)
		{
			resource rci = obb.getResourceInfo(rcProduced.get(i));
			rci.owner = contract.getClientName();
			iMsg.getContentXml().resources.add(rci);
		}
		
		mbp.message.contract cntr = obb.getContractInfo(contract);
		iMsg.getContentXml().contracts.add(cntr);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}

	/**
	 * Wyslanie do klienta rezygnacji. Rezygnacja moze dotyczyc
	 * @param agentName
	 * @param uuid
	 */
	private void sendReject(String agentName, String uuid, List<Resource> rcToGiveBack)
	{
		if(uuid == null)uuid = "0";
		Message iMsg = new Message(MessageType.REJECT, this.getLocalName(), agentName);
	
		extra ex = new extra();
		ex.name = "uuid";
		ex.value = uuid;
		iMsg.getContentXml().extras.add(ex);
		
		if(rcToGiveBack != null)
		{
			ObjectBuilder obb = new ObjectBuilder();
			for(int i = 0; i<rcToGiveBack.size(); i++)
			{
				iMsg.getContentXml().resources.add(obb.getResourceInfo(rcToGiveBack.get(i)));
			}
		}
		
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	private void sendNoOffer(String agentName)
	{
		Message iMsg = new Message(MessageType.NOOFFER, this.getLocalName(), agentName);
		SenderLogger.send(this, iMsg.GetACLMessage());
	}
	
	private void sendResourceGet(String storageName, String owner, ResourceValidation rcv)
	{					
		Message message = new Message(MessageType.RESOURCEGET, getLocalName(), storageName);
		extra ex1 = new extra();
		ex1.name = "owner";
		ex1.value = owner;
		extra ex2 = new extra();
		ex2.name = "validation";
		ex2.value = "";
		message.getContentXml().extras.add(ex1);
		message.getContentXml().extras.add(ex2);			
		message.setResourceValidation(rcv);
		ACLMessage msg = message.GetACLMessage();
		SenderLogger.send(this, msg);
	}
	
//	private void sendResourceDel(String uuid, String agentName)
//	{
//		Message iMsg = new Message(MessageType.RESOURCEDEL, this.getLocalName(), agentName);
//		resource rc = new resource();
//		rc.resourceid = uuid;
//		iMsg.getContentXml().resources.add(rc);
//		SenderLogger.send(this, iMsg.GetACLMessage());
//	}
	
	//----------------------------------------------------------------------------------------
	
	/**
	 * Implementuje zadania wykonywane czyklicznie przez agenta
	 * @author Wojciech Wilanski
	 *
	 */
	public class ServiceServerTicker extends TickerBehaviour
	{
		

		public ServiceServerTicker(Agent a, long period) {
			super(a, period);
			// TODO Auto-generated constructor stub
		}

		@Override
		protected void onTick() 
		{
			//sprawdzenie ofert
			checkOffers();
						
			//sprawdzenie kontraktow
			processContracts();
		}
		
		/**
		 * Sprawdzenie wyslanych ofert pod katem waznosci
		 */
		public void checkOffers()
		{
			//logger.debug("Checking offers...");
			Iterator<String> it  = offersList.keySet().iterator();
			List<String> offersToRemove = new ArrayList<String>();
			while(it.hasNext())
			{
				String key = it.next();
				if(!offersList.get(key).isOfferValid())
				{
					offersToRemove.add(key);
				}
			}
			
			for(int i = 0; i<offersToRemove.size(); i++)
			{
				offersList.remove(offersToRemove.get(i));
				logger.info("Removed offer: " + offersToRemove.get(i));
			}
		}
		
		
		public void processContracts()
		{
			Iterator<String> it = contractsList.keySet().iterator();
			List<String> toRemove = new ArrayList<String>();
			
			while(it.hasNext())
			{
				String key = it.next();
				Contract contract = contractsList.get(key);
				contract.validateContract();
				//String owner = contract.getClientName();
				
				ContractState state = contract.getContractState();
				
				
				switch(state)
				{
				case NEW: //proba podpisania kontraktu
					processContractNew(contract);
					break;
				case REALIZE:
					processContractRealize(contract);				
					break;
				case PRODUCE:
					processContractProduce(contract);
					break;
				case FROZEN:
					logger.warn("Contract: " + contract.GetUUID() + " FROZEN");
					//sendContract(contract);
					break;
				case CLOSED:
					toRemove.add(new String(key));
					break;
				case BROKEN:
					processContractBroken(contract);
					//toRemove.add(new String(key));
					break;
				case REJECTED:
					processNegotiation(Activity.CLIENT, MessageType.REJECT, key);
					toRemove.add(new String(key));
					break;
				}
				
			}
			
			for(int i = 0; i<toRemove.size(); i++)
			{
				Contract contract = contractsList.remove(toRemove.get(i));
				contractsArchiv.put(new String(contract.GetUUID()), contract);
			}
		}
		
		/**
		 * Przetworzenie nowo utworzonego kontraktu. Jezeli przetworzenie zakonczy sie pozytywnie
		 * zostanie wyslany podpisany kontrakt do klienta
		 * @param contract
		 */
		private void processContractNew(Contract contract)
		{	
			// Utworzenie listy na zasoby zaliczka
			List<Resource> rcAdvanceList = new ArrayList<Resource>();
			
			// owner - nazwa agenta ktory przyslal zasob, czyli nazwa klienta kontraktu
			String owner = new String( contract.getClientName() );
			
			// Przejscie przez liste zaliczka
			for(int i = 0; i<contract.getOffer().getResourcesIn(0).size(); i++)
			{
				// Pobranie typu zasobu
				String type = contract.getOffer().getResourcesIn(0).get(i).getType().getResourceTypeName();
				
				int quantity = contract.getOffer().getResourcesIn(0).get(i).getQuantity();
				if(quantity==0)
				{
					rcAdvanceList.add(null);
					continue;
				}
				
				// Przejscie przez liste posiadanych zasobow
				Iterator<String> itRc = resourceList.keySet().iterator();
				while(itRc.hasNext())
				{
					String uuid = itRc.next();
					Resource rc = resourceList.get(uuid);
					if((rc.getType().getResourceTypeName().compareTo(type) == 0) && (rc.getOwner().compareTo(owner) == 0))
					{
						try {
							if(contract.getOffer().getResourcesIn(0).get(i).validateCondition(rc))
							{
								rcAdvanceList.add(rc);
								
								// Sprawdzenie czy sa juz wszystkie zasoby potrzebne jako zaliczka
								if(rcAdvanceList.size() == contract.getOffer().getResourcesIn(0).size())
								{
									break;
								}
							}
						} catch (ParseExpressionErrorException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
			
			// Wykonuje sie jezeli brak dostarczonej zaliczki
			if(rcAdvanceList.size() != contract.getOffer().getResourcesIn(0).size())
			{
				if(contract.counter > 10)
				{
					// Wyslanie odrzucenia podpisania kontraktu - brak zaliczki
					contract.setContractState(ContractState.REJECTED);
					sendReject(contract.getAgentName(), contract.GetUUID(), null);
					logger.warn("Can not sign contract:  - Setting contract to state REJECTED");
				}
				else
				{
					for(int i = 0; i<contract.getOffer().getResourcesIn(0).size(); i++)
					{
						ResourceValidation rcv = contract.getOffer().getResourcesIn(0).get(i);					
						sendResourceGet(storageName, owner, rcv);
						logger.info("Sending request for resource advance: " + rcv.getType().getResourceTypeName() + " to " + storageName);
					}
					contract.counter++;
				}
				return;
			}
			
			// Jezeli podpisanie sie powiedzie zostanie zmieniony stan kontraktu
			if(contract.signContract(rcAdvanceList))
			{	
				sendAcceptContract(contract);
				contract.counter = -1;
				logger.info("Contract: " + contract.GetUUID() + " signed.");
			}
			else
			{
				logger.error("Contract: " + contract.GetUUID() + " not signed");
			}
		}
		
		/**
		 * Przetwarzanie kontraktu w fazie realizacji
		 * @param contract
		 */
		private void processContractRealize(Contract contract)
		{
			//logger.debug("processContracts() REALIZE");
			String key = contract.GetUUID();
			String owner = contract.getClientName();
			
			//pobranie listy zasobow ktore nie zostaly jeszcze dostarczone
			List<String> rcNotRecieved = contract.getNotRecievedStuff();
			
			//pobranie dat dostarczenia zasobow (stuff)
			List<Date> rcStuffDateList = contract.getstuffValidDate();
			
			//pobranie listy warunkow dla zasobow (stuff)
			List<ResourceValidation> rvValidStuffList = contract.getOffer().getResourcesIn(1);
			
			//pobranie ideksu od ktorego zasoby nie zostaly jeszcze dostarczone
			int index = rcStuffDateList.size() - rcNotRecieved.size();
			
			//Pobranie aktualnej daty
			//Calendar calendar = Calendar.getInstance();
			Date today = new Date();
			
//			logger.debug("SIZES --> index=" + index + 
//					" rcStuffList=" + rcStuffList.size() +
//					" rcStuffDateList=" + rcStuffDateList.size() +
//					" rvValidStuffList=" + rvValidStuffList.size());
			
			//przetwarzanie dla zasobow stuff od tych ktore nie zostaly jeszcze dostarczone
			for(int i = index; i<rcStuffDateList.size(); i++)
			{
				boolean rcRecieved = false;
				//String type = rcNotRecieved.get(i - index);
				
				ResourceValidation rcv = rvValidStuffList.get(i);
				
				// Sprawdzenie ilosci dobra w zasobie
				int quantity = rcv.getQuantity();
				if(quantity==0)
				{
					if(contract.processContract(null))
					{
						sendContract(contract);
						return;
					}
				}
				
				//obliczenie milisec ile jescze zostalo do terminu dostarczenia zasobu
				long diff = rcStuffDateList.get(i).getTime() - today.getTime();
				
				//logger.debug("Stuff [" + i + "]----------- diff:" + diff);
				
				// zostalo 25 sec
				if(diff<25000 && diff>=20000)
				{
					if(contract.counter < i)
					{
						//ResourceValidation rcv = rvValidStuffList.get(i);
						sendResourceGet(storageName, owner, rcv);
						logger.info("Sending request for resource stuff (25 sec before): " + rcv.getType().getResourceTypeName() + " to " + storageName);
						contract.counter = i;
					}
				}
				
				//zostalo 20 sec
				if(diff<20000)
				{
					String type = rcNotRecieved.get(i - index);
					//ResourceValidation rcv = rvValidStuffList.get(i);
					//sendResourceGet(storageName, owner, rcv);
					//sprawdzeneie czy zasob zostal juz pobrany z magazynu
					
					//boolean rcRecieved = false;
					Iterator<String> itRc = resourceList.keySet().iterator();
					while(itRc.hasNext())
					{
						String uuid = itRc.next();
						Resource rc = resourceList.get(uuid);
						if((rc.getType().getResourceTypeName().compareTo(type) == 0) && (rc.getOwner().compareTo(owner) == 0))
						{
							try {
								if(rcv.validateCondition(rc))
								{
									if(contract.processContract(rc))
									{										
										//resourceList.remove(uuid);
										//sendResourceDel(rc.getUUID(), storageName);
										sendContract(contract);
										rcRecieved = true;
										break;
									}
								}
							} catch (ParseExpressionErrorException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
					
					
					if(rcRecieved == false)
					{
						sendResourceGet(storageName, owner, rcv);
						logger.info("Sending request for resource stuff: " + rcv.getType().getResourceTypeName() + " to " + storageName);
						break;
					}
					
					//wyslanie zadania do magazynu.
					//ResourceValidation rcv = rvValidStuffList.get(i);					
					
					//sendResourceGet(storageName, owner, rcv);
					//logger.info("Sending request for resource stuff: " + rcv.getType().getResourceTypeName() + " to " + storageName);					
				}
				
			}
		}
		
		private void processContractProduce(Contract contract)
		{
			String key = contract.GetUUID();
			String owner = contract.getClientName();
			Date today = new Date();
			
			//obliczenie milisec ile jescze zostalo do terminu realizacji uslugi
			long diff = contract.getrealzeValidDate().getTime() - today.getTime();
			
			List<ResourceValidation> rvValidPayList = contract.getOffer().getResourcesIn(2);
			
			List<Resource> rcPayList = new ArrayList<Resource>();
			
			
			//przekroczony termin
//			if(diff<0)
//			{
//				contract.setContractState(ContractState.BROKEN);
//				logger.warn("Timeout. Not recieved resources requied for payment - Setting contract to state BROKEN");
//				contractsArchiv.put(key, contract);
//				contractsList.remove(key);
//				return;
//			}
			
			if(diff<15000 && diff>=10000)
			{
				if(contract.counter == 0)
				{
					for(int i = 0; i<rvValidPayList.size(); i++)
					{
						ResourceValidation rcv = rvValidPayList.get(i);
						int quantity = rcv.getQuantity();
						// Wysylane jest zadanie do magazynu jedynie po zasoby ktorych ilosc > 0 
						if(quantity>0)
						{
							sendResourceGet(storageName, owner, rcv);
							logger.info("Sending request for resource pay: (15 sec before)" + rcv.getType().getResourceTypeName() + " to " + storageName);
						}
					}
					contract.counter = 1;
				}
			}
			
			
			//zostalo 10 sec
			if(diff<10000)
			{
				for(int i = 0; i<rvValidPayList.size(); i++)
				{
					String type = rvValidPayList.get(i).getType().getResourceTypeName();
					
					// Jezeli ilosc dobra w zasobie = 0
					int quantity = rvValidPayList.get(i).getQuantity();
					if(quantity==0)
					{
						rcPayList.add(null);
						continue;
					}
					
					Iterator<String> itRc = resourceList.keySet().iterator();
					while(itRc.hasNext())
					{
						String uuid = itRc.next();
						Resource rc = resourceList.get(uuid);
						if((rc.getType().getResourceTypeName().compareTo(type) == 0) && (rc.getOwner().compareTo(owner) == 0))
						{
							try {
								if(rvValidPayList.get(i).validateCondition(rc))
								{
									rcPayList.add(rc);
								}
							} catch (ParseExpressionErrorException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						
						}				
					}
				}
				
				if(rcPayList.size() == rvValidPayList.size())
				{
					logger.info("Finalizing contract: " + contract.GetUUID());
					List<Resource> rcProduceList = contract.finalizeContract(rcPayList);
//					for(int i = 0; i<rcProduceList.size(); i++)
//					{
//						Message message = new Message(MessageType.RESOURCE, getLocalName(), storageName);
//						message.setResource(rcProduceList.get(i));
//						ACLMessage msg = message.GetACLMessage();
//						SenderLogger.send(this, msg);
//						logger.info("Sending produced resource: " + rcProduceList.get(i).getType().getResourceTypeName() + " to: " + storageName);
//					}
					
					contract.setContractState(ContractState.CLOSED);
					changeOwner(contract.getClientName(), getLocalName());
					sendDone(contract, rcProduceList);
					
					//contractsArchiv.put(key, contract);
					//contractsList.remove(key);
					logger.info("Finalize contract: " + contract.GetUUID() + " finished.");
				}
				else
				{
					//rcPayList.clear();
					for(int i = 0; i<rvValidPayList.size(); i++)
					{
						ResourceValidation rcv = rvValidPayList.get(i);					
						sendResourceGet(storageName, owner, rcv);
						logger.info("Sending request for resource pay: " + rcv.getType().getResourceTypeName() + " to " + storageName);
					}
				}
				
			}
		}
		
		private void processContractBroken(Contract contract)
		{
			String key = contract.GetUUID();
			//logger.warn("Timeout. Not recieved resource: " + rcStuffList.get(i - index)+ " - Setting contract to state BROKEN");
			//contractsArchiv.put(key, contract);
			//contractsList.remove(key);
			//processNegotiation(Activity.CLIENT, MessageType.REJECT, key);
			sendContract(contract);
			contract.setContractState(ContractState.REJECTED);
			
			//sendReject(contract.getAgentName(), key, null);
		}
		
	} //ServiceServerTicker

}
